/**
 * 
 */
package server.connection;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.StringTokenizer;


import server.connection.protocol.Event;
import server.connection.protocol.EventType;
import server.exception.ServerException;

/**
 * Module that defines net protocol.  
 * 
 * Protocol module is implemented as a standalone Protocol object. 
 * 
 * @author Kamil Nowak
 *
 */
public class Protocol {

	private final static Protocol instance = new Protocol();
	
	private HashMap<String,EventType> string2type = new HashMap<String,EventType>();
	
	private HashMap<EventType,String> type2string = new HashMap<EventType,String>();
	
	{
		
		string2type.put("HELLO",EventType.HELLO);
		string2type.put("LOGIN",EventType.LOGIN);
		string2type.put("OK",EventType.OK);
		string2type.put("LOGINOK",EventType.LOGINOK);
		string2type.put("LOGINERR",EventType.LOGINERR);
		string2type.put("TABLES",EventType.TABLES);
		string2type.put("GETTABLES",EventType.GETTABLES);
		string2type.put("SMSG",EventType.SMSG);
		string2type.put("MSG",EventType.MSG);
		string2type.put("PROMO",EventType.PROMO);
		string2type.put("PROMOOK",EventType.PROMOOK);
		string2type.put("PROMOERR",EventType.PROMOERR);
		string2type.put("CREATE",EventType.CREATE);
		string2type.put("CREATEOK",EventType.CREATEOK);
		string2type.put("CREATEERR",EventType.CREATEERR);
		string2type.put("JOIN",EventType.JOIN);
		string2type.put("JOINOK",EventType.JOINOK);
		string2type.put("JOINERR",EventType.JOINERR);
		string2type.put("LEAVETABLE",EventType.LEAVETABLE);
		string2type.put("ENDGAME",EventType.ENDGAME);
		string2type.put("MOVE",EventType.MOVE);
		string2type.put("MOVEOK",EventType.MOVEOK);
		string2type.put("MOVEERR",EventType.MOVEERR);
		string2type.put("FINISHGAME",EventType.FINISHGAME);
		string2type.put("FINISHGAMEOK",EventType.FINISHGAMEOK);
		string2type.put("FINISHGAMEERR",EventType.FINISHGAMEERR);
		string2type.put("TIMEOUT",EventType.TIMEOUT);
		string2type.put("EXIT",EventType.EXIT);
		
		type2string.put(EventType.OK,"OK");
		type2string.put(EventType.ERR,"ERR");
		type2string.put(EventType.LOGINOK,"LOGINOK");
		type2string.put(EventType.LOGINERR,"LOGINERR");
		type2string.put(EventType.TABLES,"TABLES");
		type2string.put(EventType.MSG,"MSG");
		type2string.put(EventType.PROMO,"PROMO");
		type2string.put(EventType.PROMOOK,"PROMOOK");
		type2string.put(EventType.PROMOERR,"PROMOERR");
		type2string.put(EventType.CREATEOK,"CREATEOK");
		type2string.put(EventType.CREATEERR,"CREATEERR");
		type2string.put(EventType.JOIN,"JOIN");
		type2string.put(EventType.JOINOK,"JOINOK");
		type2string.put(EventType.JOINERR,"JOINERR");
		type2string.put(EventType.MOVE,"MOVE");
		type2string.put(EventType.MOVEOK,"MOVEOK");
		type2string.put(EventType.MOVEERR,"MOVEERR");
		type2string.put(EventType.ENDGAME,"ENDGAME");
		type2string.put(EventType.FINISHGAME,"FINISHGAME");
		type2string.put(EventType.FINISHGAMEOK,"FINISHGAMEOK");
		type2string.put(EventType.FINISHGAMEERR,"FINISHGAMEERR");
		
	}
	
	private Protocol() {}


	/**
	 * Gets standalone instance of Protocol class
	 * @return standalone object
	 */
	public static Protocol instance() {
		return instance;
	}

	/**
	 * Parses message end creates event based on the message body
	 * 
	 * @param message string to parse
	 * @return parsed event
	 * @throws ServerException if received improper token
	 */
	public Event parse(String message) throws ServerException {
		
		StringTokenizer tokenizer = new StringTokenizer(message,"|\t\n\r\f");
		Event event = new Event();	
		
		if (tokenizer.hasMoreTokens())	event.setType( string2Type(tokenizer.nextToken()) );
		
		if (event.getType() == null) throw new ServerException(25);
		
		LinkedList<String> params = new LinkedList<String>();
		while (tokenizer.hasMoreTokens()) {
			params.add(tokenizer.nextToken());	
		}
		event.setParams(params.toArray( new String[ params.size() ] ));
		
		return event;
	}
	
	private EventType string2Type(String foo) {
		
		return string2type.get(foo); 

	}
	
	private String type2string(EventType foo) {
		return type2string.get(foo); 
	}
	
	/**
	 * Creates message based on the event provided 
	 * 
	 * @param event event to process
	 * @return message
	 */
	public String createMessage(Event event) {
		StringBuilder msg;
		
		msg = new StringBuilder(type2string(event.getType()));
		
		if (event.getParams() != null) {
			for (String param :  event.getParams())
				msg.append('|').append(param);
		}
		
		return msg.append("\r\n").toString();
	}

}
